package com.liankuan.console.service.ServiceImpl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.liankuan.console.common.ResultEnum;
import com.liankuan.console.controller.vo.StrategyListVO;
import com.liankuan.console.controller.vo.StrategyOtherVO.StrategyInfoVO;
import com.liankuan.console.controller.vo.StrategyVO;
import com.liankuan.console.controller.vo.request.*;
import com.liankuan.console.controller.vo.request.Strategy.ExchangeIO;
import com.liankuan.console.controller.vo.request.Strategy.SaveStrategyIO;
import com.liankuan.console.dto.*;
import com.liankuan.console.exception.ServiceException;
import com.liankuan.console.repository.*;
import com.liankuan.console.service.StrategyService;
import com.liankuan.console.service.UserService;
import com.liankuan.console.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StrategyServiceImpl implements StrategyService {
    private static final Integer STRATEGY_LEVEL_CREATE = 1;

    private final StrategyRepository strategyRepository;
    private final ExchangeRepository exchangeRepository;
    private final StrategyExchangeRepository strategyExchangeRepository;
    private final UserStrategyServiceImpl userStrategyService;
    private final ReadFileUtil readFileUtil;
    private final StrategyShell strategyShell;
    private final Config config;
    private final UserExchangeRepository userExchangeRepository;
    private final UserStrategyRepository userStrategyRepository;
    private final UserRoleServiceImpl userRoleService;
    private final UserService userService;

    @Autowired
    public StrategyServiceImpl(StrategyRepository strategyRepository, ExchangeRepository exchangeRepository,
                               StrategyExchangeRepository strategyExchangeRepository, ReadFileUtil
                                       readFileUtil, StrategyShell strategyShell, Config config,
                               UserStrategyServiceImpl userStrategyService,
                               UserExchangeRepository userExchangeRepository, UserStrategyRepository
                                       userStrategyRepository, UserRoleServiceImpl userRoleService, UserService
                                       userService) {
        this.strategyRepository = strategyRepository;
        this.exchangeRepository = exchangeRepository;
        this.strategyExchangeRepository = strategyExchangeRepository;
        this.readFileUtil = readFileUtil;
        this.strategyShell = strategyShell;
        this.config = config;
        this.userStrategyService = userStrategyService;
        this.userExchangeRepository = userExchangeRepository;
        this.userStrategyRepository = userStrategyRepository;
        this.userRoleService = userRoleService;
        this.userService = userService;
    }

    /**
     * 获取所有的策略
     *
     * @return
     */
    @Override
    public List<StrategyListVO> findAll() {
        List<Strategy> strategies = strategyRepository.findAll();
        //这样可以减少内存的引用的占用
//        StrategyListVO strategyListVO;
        return strategies.stream().map(record -> {
            StrategyListVO strategyListVO = new StrategyListVO(record);

            //现获取运行状态
            ImmutablePair<String, String> immutablePair = getStrategyState(record);
            Integer state = JarStateUtil.typeMap.get(immutablePair.getLeft());
            strategyListVO.setState(JarStateUtil.stateMap.get(state));

            //获取策略的创建者
            User user = getStrategyCreator(record.getId());
            if (user == null) {
                //如果没有找到对应的创建者则默认都由admin创建
                strategyListVO.setCreator(config.getAdmin());
            } else {
                strategyListVO.setCreator(user.getName());
            }

            return strategyListVO;
        }).collect(Collectors.toList());
    }


    private User getStrategyCreator(int strategyId) {
        //获取策略的创建者
        UserStrategy userStrategy = userStrategyRepository.findByStrategyIdAndLevel(strategyId, STRATEGY_LEVEL_CREATE);

        if (userStrategy == null) {
            return null;
        }
        User user = userService.findUserById(userStrategy.getUserId());

        return user;
    }

    /**
     * 获取策略的状态文件中运行状态+是否更新版本的状态
     *
     * @param strategy
     * @return
     */
    private ImmutablePair<String, String> getStrategyState(Strategy strategy) {
        String jarStatePath;

        if (config.getEnv().equals("pro")) {
            //正式
            String strategyName = strategy.getJarName().substring(0, strategy.getJarName().length() - 4) + "-" +
                    strategy
                            .getId();
            jarStatePath = String.format(config.getJarstatePath(), strategyName);
        } else {
            // TODO 测试
            jarStatePath = config.getJarstatePath();
        }
        String jarState;
        String jarUpdateSate;
        //读取状态信息--->等读取完成之后再继续执行其他逻辑
        try {
            List<String> stateArray = ReadFileUtil.getUpdateJarState(jarStatePath);
            jarState = stateArray.get(0);

            //jar包是否有更新
            jarUpdateSate = stateArray.get(1);

            return ImmutablePair.of(jarState, jarUpdateSate);
        } catch (Exception e) {
            throw new ServiceException(ResultEnum.READ_FILE_FAILED);
        }
    }

    /**
     * 根据策略ID获取策略的详细信息
     *
     * @param id
     * @return
     */
    @Override
    public StrategyVO findOne(int id) {
        //获取策略的基本信息
        Strategy strategy = strategyRepository.findById(id);
        /**
         * 首先(通过策略名)从指定文件中获取对应的策略对应的日志文件的真正运行状态
         *
         */
        ImmutablePair<String, String> immutablePair = getStrategyState(strategy);
        String jarState = immutablePair.getLeft();
        String jarUpdateSate = immutablePair.getRight();


        //转为前端页面需要显示的----》赋值给策略对象
        Integer state = JarStateUtil.typeMap.get(jarState);

        strategy.setState(state);

        //获取策略对应的所有的交易所ID信息
        List<StrategyExchange> strategyExchanges = strategyExchangeRepository.findAllByStrategyId(id);

        //通过exchange的id获取策略对应的所有交易所的基本信息
        List<Exchange> exchanges = new ArrayList<>();
        strategyExchanges.forEach(record -> {
            Exchange exchange = exchangeRepository.findById(record.getExchangeId());
            exchanges.add(exchange);
        });
        return new StrategyVO(strategy, strategyExchanges, exchanges, jarUpdateSate);
    }

    /**
     * 转为策略端的获取策略信息
     *
     * @param params
     * @return
     */
    @Override
    public StrategyInfoVO findOne(String params) {
        //获取策略的基本信息
        Strategy strategy = strategyRepository.findByParams(params);

        if (strategy == null) {
            throw new ServiceException(ResultEnum.NOT_STRATEGY);
        }

        //获取策略对应的所有的交易所ID信息
        List<StrategyExchange> strategyExchanges = strategyExchangeRepository.findAllByStrategyId(strategy.getId());

        //通过exchange的id获取策略对应的所有交易所的基本信息
        List<Exchange> exchanges = new ArrayList<>();
        strategyExchanges.forEach(record -> {
            Exchange exchange = exchangeRepository.findById(record.getExchangeId());
            exchanges.add(exchange);
        });
        return new StrategyInfoVO(strategy, strategyExchanges, exchanges);
    }

    /**
     * 更新策略的启动状态
     *
     * @param id
     * @param state
     */
    @Override
    public void updateStrategyState(int id, int state) {
        String jarState;
        //修改为关闭状态
        if (state == 0) {
            //都是启动jar包所以定义状态为
            jarState = "PAUSE";
        } else {
            //修改为启动状态
            jarState = "RUN";
        }

        //获取该策略对应的jar包运行所需要的参数,jar报名等等
        Strategy strategy = strategyRepository.findById(id);
        strategyShell.UpdateStrategySateShell(jarState, strategy);
    }

    /**
     * 重启策略
     *
     * @param id
     * @param state
     */
    @Override
    public void restartStrategyState(int id, int state) {
        //启动jar包所以定义状态为
        String jarState = "RESTART";
        //获取该策略对应的jar包运行所需要的参数,jar报名等等
        Strategy strategy = strategyRepository.findById(id);

        strategyShell.UpdateStrategySateShell(jarState, strategy);

    }


    /**
     * 保存策略的整体修改信息
     *
     * @param strategyVO
     */
    @Transactional
    @Override
    public void save(StrategyVO strategyVO) {
        saveStrategy(strategyVO);
        saveStrategyExchange(strategyVO);
    }

    /**
     * 更新策略对应的交易所信息
     *
     * @param strategyExchangeIO
     */
    @Override
    public void updateExchange(StrategyExchangeIO strategyExchangeIO) {
        //如果该记录库中已经存在则进行更新否则进行插入
        StrategyExchange strategyExchange = strategyExchangeRepository.
                findAllByStrategyIdAndExchangeId(strategyExchangeIO.getStrategyId(), strategyExchangeIO.getExchangeId
                        ());
        if (strategyExchange != null) {
            //已存在(进行修改)
            strategyExchangeRepository.updateStrategyExchange(strategyExchangeIO.getStrategyId(), strategyExchangeIO
                            .getExchangeId(),
                    strategyExchangeIO.getPairs());
        } else {
            //新增
            StrategyExchange strategyExchange1 = new StrategyExchange();
            strategyExchange1.setStrategyId(strategyExchangeIO.getStrategyId());
            strategyExchange1.setExchangeId(strategyExchangeIO.getExchangeId());
            strategyExchange1.setPairs(strategyExchangeIO.getPairs());
            strategyExchangeRepository.save(strategyExchange1);
        }

    }

    /**
     * 更新策略的基本的信息
     *
     * @param strategyBasicIO
     */
    @Override
    public void updateStrategyBasic(StrategyBasicIO strategyBasicIO) {
        strategyRepository.updateStrategyBasic(strategyBasicIO.getId(), strategyBasicIO.getName(),
                strategyBasicIO.getJarName(), strategyBasicIO.getEmail(), strategyBasicIO.getParams());

    }

    /**
     * 更新策略的配置信息
     *
     * @param strategyConfigIO
     */
    @Override
    public void updateStrategyConfig(StrategyConfigIO strategyConfigIO) {
        //检验配置参数的数据类型是否合法
        checkStrategyConfig(strategyConfigIO.getConfig());

        strategyRepository.updateStrategyConfig(strategyConfigIO.getConfig(), strategyConfigIO
                .getStrategyId());
    }

    /**
     * 校验策略配置参数是否合法
     *
     * @param configJson
     */
    private void checkStrategyConfig(String configJson) {
        //检验配置参数的数据类型是否合法
        JSONArray array;
        try {
            array = JSON.parseArray(configJson);
        } catch (JSONException e) {
            throw new ServiceException(ResultEnum.PARAM_ILLEGAL);
        }

        for (int i = 0; i < array.size(); i++) {
            JSONObject object = array.getJSONObject(i);
            if (!checkParam(object.getString("type"), object.getString("value"))) {
                System.out.println(object);
                throw new ServiceException(ResultEnum.PARAM_ILLEGAL);
            }
        }
    }

    /**
     * 校验数值类型是否合法
     *
     * @param type
     * @param value
     * @return
     */
    private Boolean checkParam(String type, String value) {

        if ("number".equals(type)) {
            return value.matches("-?[0-9]+.*[0-9]*");
        } else if ("json".equals(type)) {
            return CheckParamUtil.isJSONValid(value);
        }
        return true;
    }

    /**
     * 获取策略对应的日志信息
     *
     * @param strategyLogIO
     * @return
     */
    @Override
    public HashMap getStrategyLogInfo(StrategyLogIO strategyLogIO) {

        //先获取策略的日志文件名
        Strategy strategy = strategyRepository.findOne(strategyLogIO.getStrategyId());
        return readFileUtil.lodingLogFile(strategyLogIO.getPageNumber(), strategyLogIO.getPageSize(), strategy);
    }

    /**
     * 删除策略对应的交易所
     *
     * @param strategyId
     * @param exchangeId
     */
    @Override
    public void deleteExchange(int strategyId, int exchangeId) {
        strategyExchangeRepository.deleteByStrategyIdAndExchangeId(strategyId, exchangeId);
    }

    /**
     * 在为策略新增交易所获取供选择的所以交易所信息
     *
     * @param strategyId
     * @return
     */
    @Override
    public StrategyAddExchangeVO finAllExchange(int strategyId) {
        //获取该策略所包含的交易所信息
        List<StrategyExchange> strategyExchanges = strategyExchangeRepository.findAllByStrategyId(strategyId);

        // 获取所有的交易所信息
        List<Exchange> exchanges = exchangeRepository.findAll();

        return new StrategyAddExchangeVO(strategyExchanges, exchanges);
    }

    /**
     * 在为策略新增交易所获取供选择的所以交易所信息(交易所与用户绑定之后)
     *
     * @param strategyId
     * @param userId
     * @return
     */
    @Override
    public StrategyAddExchangeVO finAllExchange(Integer strategyId, Integer userId) {
        //获取用户角色
        UserRole userRole = userRoleService.findRoleByUserId(userId);

        // TODO 超级管理员则可以控制显示所有的交易所
        if (config.getAdmin().equals(userRole.getRole())) {
            return finAllExchange(strategyId);
        }

        //获取该策略所包含的交易所信息
        List<StrategyExchange> strategyExchanges = strategyExchangeRepository.findAllByStrategyId(strategyId);

        //获取改用户所持有的交易所
        List<Integer> userExchangeIds = userExchangeRepository.findAllByUserId(userId).stream().map
                (UserExchange::getExchangeId).collect(Collectors.toList());

        //根据交易所的id集合去获取交易所信息
        List<Exchange> exchanges = exchangeRepository.findAllByIdInOrderById(userExchangeIds);

        return new StrategyAddExchangeVO(strategyExchanges, exchanges);
    }

    /**
     * 获取用户对该策略的操作级别
     *
     * @param strategyId
     * @return
     */
    @Override
    public Integer getUserStrategyLevel(Integer strategyId, Integer userId) {
        UserStrategy userStrategy = userStrategyRepository.findByUserIdAndStrategyId(userId, strategyId);
        if (userStrategy == null) {
            //证明是超级管理员
            return config.getManager();
        }

        return userStrategy.getLevel();
    }

    /**
     * 日志的下载
     *
     * @param request
     * @param response
     * @param strategyId
     */
    @Override
    public void downloadFile(HttpServletRequest request, HttpServletResponse response, Integer strategyId) {
        Strategy strategy = strategyRepository.findById(strategyId);
        readFileUtil.downloadFile(request, response, strategy);

    }


    @Override
    public void saveStrategy(SaveStrategyIO saveStrategyIO) {

    }

    /**
     * 储存策略信息
     *
     * @param saveStrategyIO
     */
    @Override
    @Transactional
    public void saveStrategy(SaveStrategyIO saveStrategyIO, int userId) {
        //检验策略配置参数的数据类型是否合法
        checkStrategyConfig(saveStrategyIO.getStrategyInfo().getConfigs());

        Strategy strategy = new Strategy(saveStrategyIO.getStrategyInfo());
        //进行空值的校验
        if (strategy.getName() == null || strategy.getParams() == null || strategy.getJarName() == null || strategy
                .getEmail() == null) {
            throw new ServiceException(ResultEnum.STRATEGY_BASIC_INFO_NOT_NULL);
        }

        //首先得去新增信息入库-->>>>因为得用ID去生成策略目录
        strategyRepository.save(strategy);

        //启动创建策略目录以及拷贝对应jar包的脚本 -->正式环境下使用
        if (config.getEnv().equals("pro")) {
            strategyShell.CreateStrategyShell(strategy);
        }

        //存储对应的交易所信息
        List<StrategyExchange> strategyExchanges = new ArrayList<>();
        for (int i = 0; i < saveStrategyIO.getExchanges().size(); i++) {
            ExchangeIO exchangeIO = saveStrategyIO.getExchanges().get(i);
            //如果没有选择交易所则不进行入库
            if (exchangeIO.getExchangeId() == null || exchangeIO.getPairs() == null) {
                continue;
            }
            StrategyExchange strategyExchange = new StrategyExchange();
            strategyExchange.setStrategyId(strategy.getId());
            strategyExchange.setExchangeId(exchangeIO.getExchangeId());
            strategyExchange.setPairs(exchangeIO.getPairs());

            strategyExchanges.add(strategyExchange);
        }

        //如果策略有对应的交易所信息才进行关联入库入库
        if (!strategyExchanges.isEmpty()) {
            strategyExchangeRepository.save(strategyExchanges);
        }

        //存入改策略是谁创建的---》给其设置对应的默认操作级别
        userStrategyService.createUserStrategy(strategy, userId);
    }

    /**
     * 删除策略信息  (所有的映射关系全部删除即可)
     *
     * @param id
     */
    @Override
    @Transactional
    public void deleteStrategy(Integer id) {
        //TODO 应该是首先触发脚本去删除对应的策略目录--》正式环境下触发脚本
        if (config.getEnv().equals("pro")) {
            Strategy strategy = strategyRepository.findById(id);
            //触发脚本
            String state = "DELETE";
            strategyShell.UpdateStrategySateShell(state, strategy);
        }
        //1.删除策略对应的交易所信息
        strategyExchangeRepository.deleteAllByStrategyId(id);

        //2.删除用户和策略的关联信息
        userStrategyRepository.deleteAllByStrategyId(id);

        //3.删除该策略的基本信息
        strategyRepository.delete(id);
    }

    /**
     * 获取策略日志最新信息
     *
     * @param strategyId
     * @param lastLineNumber
     * @return
     */
    @Override
    public HashMap getNewLogInfo(Integer strategyId, Integer lastLineNumber) {
        //先获取策略的日志文件名
        Strategy strategy = strategyRepository.findOne(strategyId);

        return readFileUtil.getNewLogData(strategy, lastLineNumber);
    }

    /**
     * 读取收益曲线日志文件
     *
     * @param strategyLogIO
     * @return
     */
    @Override
    public HashMap getProfitLog(StrategyLogIO strategyLogIO) {
        //先获取策略的日志文件名
        Strategy strategy = strategyRepository.findOne(strategyLogIO.getStrategyId());
        return readFileUtil.lodingPrefitLog(strategyLogIO.getPageNumber(), strategyLogIO.getPageSize(), strategy
        );
    }

    /**
     * 获取策略收益曲线最新信息
     *
     * @param strategyId
     * @param lastLineNumber
     * @return
     */
    @Override
    public HashMap getNewProfitLog(Integer strategyId, Integer lastLineNumber) {
        //先获取策略的日志文件名
        Strategy strategy = strategyRepository.findOne(strategyId);

        return readFileUtil.getNewProfitData(strategy, lastLineNumber);
    }

    /**
     * 更新JAR包
     *
     * @param strategyId
     */
    @Override
    public void updateJar(Integer strategyId) {
        //需要去启动脚本执行更新JAR包的操作
        String jarState = "UPDATE";
        //获取该策略对应的jar包运行所需要的参数,jar报名等等
        Strategy strategy = strategyRepository.findById(strategyId);

        strategyShell.UpdateStrategySateShell(jarState, strategy);
    }

    /**
     * 8
     * 获取持久化信息
     *
     * @param strategyId
     * @return
     */
    @Override
    public StringBuilder getOrmData(Integer strategyId) {
        Strategy strategy = strategyRepository.findById(strategyId);
        String filename;
        if (config.getEnv().equals("pro")) {
            String strategyName = strategy.getJarName().substring(0, strategy.getJarName().length() - 4) + "-" +
                    strategy.getId();

            filename = String.format(config.getLogPathOrmInfo(), strategyName);
        } else {
            //TODO 测试
            filename = String.format(config.getLogPathOrmInfo());
        }

        return readFileUtil.readTxtFile(filename);
    }


    /**
     * 判断策略名是否存在
     *
     * @param name
     * @return
     */
    @Override
    public Boolean hasStrategyName(String name) {
        Strategy strategy = strategyRepository.findByName(name);
        if (strategy != null) {
            return true;
        }
        return false;
    }

    /**
     * 获取与用户绑定的策略
     *
     * @return
     */
    @Override
    public List<StrategyListVO> findAllByUserId(int userId) {
        List<UserStrategy> userStrategies = userStrategyRepository.findAllByUserId(userId);
        if (userStrategies.isEmpty()) {
            return Collections.emptyList();
        }
        return userStrategies.stream().map(record -> {
            Strategy strategy = strategyRepository.findOne(record.getStrategyId());
            StrategyListVO strategyListVO = new StrategyListVO(strategy);
            strategyListVO.setLevel(record.getLevel());

            //现获取运行状态
            ImmutablePair<String, String> immutablePair = getStrategyState(strategy);
            Integer state = JarStateUtil.typeMap.get(immutablePair.getLeft());
            strategyListVO.setState(JarStateUtil.stateMap.get(state));

            //获取策略的创建者
            User user = getStrategyCreator(strategy.getId());
            if (user == null) {
                //如果没有找到对应的创建者则默认都由admin创建
                strategyListVO.setCreator(config.getAdmin());
            } else {
                strategyListVO.setCreator(user.getName());
            }

            return strategyListVO;
        }).collect(Collectors.toList());
    }


    /**
     * 保存策略部分数据
     *
     * @param strategyVO
     */
    private void saveStrategy(StrategyVO strategyVO) {
        Strategy strategy = new Strategy(strategyVO);
        strategyRepository.save(strategy);
    }

    /**
     * 保存策略和交易所的关联部分的数据
     *
     * @param strategyVO
     */
    private void saveStrategyExchange(StrategyVO strategyVO) {
        List<StrategyExchange> strategyExchanges = new ArrayList<>();
        strategyVO.getExchanges().forEach(record -> {
            StrategyExchange strategyExchange = new StrategyExchange();
            strategyExchange.setStrategyId(strategyVO.getId());
            strategyExchange.setExchangeId(record.getId());
            strategyExchange.setPairs(getPairsString(record.getPairs()));

            strategyExchanges.add(strategyExchange);
        });
        //便利集合进行存储
        strategyExchanges.forEach(record -> {
            //如果该记录库中已经存在则进行更新否则进行插入
            StrategyExchange strategyExchange = strategyExchangeRepository.
                    findAllByStrategyIdAndExchangeId(record.getStrategyId(), record.getExchangeId());
            if (strategyExchange != null) {
                //已存在(进行修改)
                strategyExchangeRepository.updateStrategyExchange(record.getStrategyId(), record.getExchangeId(),
                        record.getPairs());
            } else {
                //新增
                strategyExchangeRepository.save(record);
            }
        });
    }


    /**
     * java8中的将一个list转为某种格式的String
     *
     * @param pairs
     * @return
     */
    private String getPairsString(List<String> pairs) {
        return String.join(",", pairs);
    }


}
